home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Blender 2.49b / blender-2.49b-windows.exe / $_4_ / .blender / scripts / mesh_unfolder.py < prev    next >
Text File  |  2009-08-31  |  49KB  |  1,583 lines

  1. #!BPY
  2. """
  3. Name: 'Unfold'
  4. Blender: 245
  5. Group: 'Mesh'
  6. Tip: 'Unfold meshes to create nets'
  7. Version:  v2.5
  8. Author: Matthew Chadwick
  9. """
  10. import Blender
  11. from Blender import *
  12. from Blender.Mathutils import *
  13. try:
  14.     import sys
  15.     import traceback
  16.     import math
  17.     import re
  18.     from math import *
  19.     import sys
  20.     import random
  21.     import xml.sax, xml.sax.handler, xml.sax.saxutils
  22.     
  23.     # annoying but need so classes dont raise errors
  24.     xml_sax_handler_ContentHandler = xml.sax.handler.ContentHandler
  25.  
  26. except:
  27.     Draw.PupMenu('Error%t|A full python installation is required to run this script.')
  28.     xml = None
  29.     xml_sax_handler_ContentHandler = type(0)
  30.  
  31. __author__ = 'Matthew Chadwick'
  32. __version__ = '2.5 06102007'
  33. __url__ = ["http://celeriac.net/unfolder/", "blender", "blenderartist"]
  34. __email__ = ["post at cele[remove this text]riac.net", "scripts"]
  35. __bpydoc__ = """\
  36.  
  37. Mesh Unfolder
  38.  
  39. Unfolds the selected mesh onto a plane to form a net
  40.  
  41. Not all meshes can be unfolded
  42.  
  43. Meshes must be free of holes, 
  44. isolated edges (not part of a face), twisted quads and other rubbish.
  45. Nice clean triangulated meshes unfold best
  46.  
  47. This program is free software; you can distribute it and/or modify it under the terms
  48. of the GNU General Public License as published by the Free Software Foundation; version 2
  49. or later, currently at http://www.gnu.org/copyleft/gpl.html
  50.  
  51. The idea came while I was riding a bike.
  52. """    
  53.  
  54. # ***** BEGIN GPL LICENSE BLOCK *****
  55. #
  56. # This program is free software; you can redistribute it and/or
  57. # modify it under the terms of the GNU General Public License
  58. # as published by the Free Software Foundation; either version 2
  59. # of the License, or (at your option) any later version.
  60. #
  61. # This program is distributed in the hope that it will be useful,
  62. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  63. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  64. # GNU General Public License for more details.
  65. #
  66. # You should have received a copy of the GNU General Public License
  67. # along with this program; if not, write to the Free Software Foundation,
  68. # Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  69. #
  70. # ***** END GPL LICENCE BLOCK *****
  71.  
  72. # Face lookup
  73. class FacesAndEdges:
  74.     def __init__(self, mesh):
  75.         self.nfaces = 0
  76.         # straight from the documentation
  77.         self.edgeFaces = dict([(edge.key, []) for edge in mesh.edges])
  78.         for face in mesh.faces:
  79.             face.sel = False
  80.             for key in face.edge_keys:
  81.                     self.edgeFaces[key].append(face)
  82.     def findTakenAdjacentFace(self, bface, edge):
  83.         return self.findAdjacentFace(bface, edge)
  84.     # find the first untaken (non-selected) adjacent face in the list of adjacent faces for the given edge (allows for manifold meshes too)
  85.     def findAdjacentFace(self, bface, edge):
  86.         faces = self.edgeFaces[edge.key()]
  87.         for i in xrange(len(faces)):
  88.             if faces[i] == bface:
  89.                 j = (i+1) % len(faces)
  90.                 while(faces[j]!=bface):
  91.                     if faces[j].sel == False:
  92.                         return faces[j]
  93.                     j = (j+1) % len(faces)
  94.         return None
  95.     def returnFace(self, face):
  96.         face.sel = False
  97.         self.nfaces-=1
  98.     def facesTaken(self):
  99.         return self.nfaces
  100.     def takeAdjacentFace(self, bface, edge):
  101.         if (edge==None):
  102.             return None
  103.         face = self.findAdjacentFace(bface, edge)
  104.         if(face!=None):
  105.             face.sel = True
  106.             self.nfaces+=1
  107.             return face
  108.     def takeFace(self, bface):
  109.         if(bface!=None):
  110.             bface.sel= True
  111.             self.nfaces+=1
  112.  
  113.     
  114. # A fold between two faces with a common edge
  115. class Fold:
  116.     ids = -1
  117.     def __init__(self, parent, refPoly, poly, edge, angle=None):
  118.         Fold.ids+=1
  119.         self.id = Fold.ids
  120.         self.refPoly = refPoly
  121.         self.poly = poly
  122.         self.srcFace = None
  123.         self.desFace = None
  124.         self.edge = edge
  125.         self.foldedEdge = edge
  126.         self.rm = None
  127.         self.parent = parent
  128.         self.tree = None
  129.         if(refPoly!=None):
  130.             self.refPolyNormal = refPoly.normal()
  131.         self.polyNormal = poly.normal()
  132.         if(angle==None):
  133.             self.angle = self.calculateAngle()
  134.             self.foldingPoly = poly.rotated(edge, self.angle)
  135.         else:
  136.             self.angle = angle
  137.             self.foldingPoly = poly
  138.         self.unfoldedEdge = self.edge
  139.         self.unfoldedNormal = None
  140.         self.animAngle = self.angle
  141.         self.cr = None
  142.         self.nancestors = None
  143.     def reset(self):
  144.         self.foldingPoly = self.poly.rotated(self.edge, self.dihedralAngle())
  145.     def getID(self):
  146.         return self.id
  147.     def getParent(self):
  148.         return self.parent
  149.     def ancestors(self):
  150.         if(self.nancestors==None):
  151.             self.nancestors = self.computeAncestors()
  152.         return self.nancestors
  153.     def computeAncestors(self):    
  154.         if(self.parent==None):
  155.             return 0
  156.         else:
  157.             return self.parent.ancestors()+1
  158.     def dihedralAngle(self):    
  159.         return self.angle
  160.     def unfoldTo(self, f):
  161.         self.animAngle = self.angle*f
  162.         self.foldingPoly = self.poly.rotated(self.edge, self.animAngle)
  163.     def calculateAngle(self):
  164.         sangle = Mathutils.AngleBetweenVecs(self.refPolyNormal, self.polyNormal)
  165.         if(sangle!=sangle):
  166.             sangle=0.0
  167.         ncp = self.refPolyNormal.cross(self.polyNormal)
  168.         dp = ncp.dot(self.edge.vector)
  169.         if(dp>0.0):
  170.             return +sangle
  171.         else:
  172.             return -sangle
  173.     def alignWithParent(self):
  174.         pass
  175.     def unfoldedNormal(self):
  176.         return self.unfoldedNormal
  177.     def getEdge(self):
  178.         return self.edge
  179.     def getFace(self):
  180.         return self.poly
  181.     def testFace(self):
  182.         return Poly.fromVectors([self.edge.v1, self.edge.v2, Vector([0,0,0])])
  183.     def unfoldedFace(self):
  184.         return self.foldingPoly
  185.     def unfold(self):
  186.         if(self.parent!=None):
  187.             self.parent.foldFace(self)
  188.     def foldFace(self, child):
  189.         child.foldingPoly.rotate(self.edge, self.animAngle)        
  190.         if(self.parent!=None):
  191.             self.parent.foldFace(child)
  192.             
  193. class Cut(Fold):
  194.     pass
  195.             
  196. # Trees build folds by traversing the mesh according to a local measure
  197. class Tree:
  198.     def __init__(self, net, parent,fold,otherConstructor=None):
  199.         self.net = net
  200.         self.fold = fold
  201.         self.face = fold.srcFace
  202.         self.poly = Poly.fromBlenderFace(self.face)
  203.         self.generations = net.generations
  204.         self.growing = True
  205.         self.tooLong = False
  206.         self.parent = parent
  207.         self.grown = False
  208.         if not(otherConstructor):
  209.             self.edges = net.edgeIteratorClass(self)
  210.     def goodness(self):
  211.         return self.edges.goodness()
  212.     def compare(self, other):
  213.         if(self.goodness() > other.goodness()):
  214.             return +1
  215.         else:
  216.             return -1
  217.     def isGrowing(self):
  218.         return self.growing
  219.     def beGrowing(self):
  220.         self.growing = True
  221.     def grow(self):
  222.         self.tooLong = self.fold.ancestors()>self.generations
  223.         if(self.edges.hasNext() and self.growing):
  224.             edge = self.edges.next()
  225.             tface = self.net.facesAndEdges.takeAdjacentFace(self.face, edge)
  226.             if(tface!=None):
  227.                 self.branch(tface, edge)
  228.             if(self.parent==None):
  229.                 self.grow()
  230.         else:
  231.             self.grown = True
  232.     def isGrown(self):
  233.         return self.grown
  234.     def canGrow(self):
  235.         return (self.parent!=None and self.parent.grown)
  236.     def getNet(self):
  237.         return self.net
  238.     def getFold(self):
  239.         return self.fold
  240.     def getFace(self):
  241.         return self.face
  242.     def branch(self, tface, edge):
  243.         fold = Fold(self.fold, self.poly, Poly.fromBlenderFace(tface), edge)
  244.         fold.srcFace = tface
  245.         self.net.myFacesVisited+=1
  246.         tree = Tree(self.net, self, fold)
  247.         fold.tree = tree
  248.         fold.unfold()
  249.         overlaps = self.net.checkOverlaps(fold)
  250.         nc = len(overlaps)
  251.         self.net.overlaps+=nc
  252.         if(nc>0 and self.net.avoidsOverlaps):
  253.             self.handleOverlap(fold, overlaps)
  254.         else:
  255.             self.addFace(fold)
  256.     def handleOverlap(self, fold, overlaps):
  257.         self.net.facesAndEdges.returnFace(fold.srcFace)
  258.         self.net.myFacesVisited-=1
  259.         for cfold in overlaps:
  260.             ttree = cfold.tree
  261.             ttree.growing = True
  262.             ttree.grow()
  263.     def addFace(self, fold):
  264.         ff = fold.unfoldedFace()
  265.         fold.desFace = self.net.addFace(ff, fold.srcFace)
  266.         self.net.folds.append(fold)
  267.         self.net.addBranch(fold.tree)
  268.         fold.tree.growing = not(self.tooLong)
  269.         if(self.net.diffuse==False):
  270.             fold.tree.grow()
  271.  
  272. # A Net is the result of the traversal of the mesh by Trees
  273. class Net:
  274.     def __init__(self, src, des):
  275.         self.src = src
  276.         self.des = des
  277.         self.firstFace = None
  278.         self.firstPoly = None
  279.         self.refFold = None
  280.         self.edgeIteratorClass = RandomEdgeIterator
  281.         if(src!=None):
  282.             self.srcFaces = src.faces
  283.             self.facesAndEdges = FacesAndEdges(self.src)
  284.         self.myFacesVisited = 0
  285.         self.facesAdded = 0
  286.         self.folds = []
  287.         self.cuts = []
  288.         self.branches = []
  289.         self.overlaps = 0
  290.         self.avoidsOverlaps = True
  291.         self.frame = 1
  292.         self.ff = 180.0
  293.         self.firstFaceIndex = None
  294.         self.trees = 0
  295.         self.foldIPO = None
  296.         self.perFoldIPO = None
  297.         self.IPOCurves = {}
  298.         self.generations = 128
  299.         self.diffuse = True
  300.         self.noise = 0.0
  301.         self.grownBranches = 0
  302.         self.assignsUV = True
  303.         self.animates = False
  304.         self.showProgress = False
  305.         self.feedback = None
  306.     def setSelectedFaces(self, faces):
  307.         self.srcFaces = faces
  308.         self.facesAndEdges = FacesAndEdges(self.srcFaces)
  309.     def setShowProgress(self, show):
  310.         self.showProgress = show
  311.     # this method really needs work
  312.     def unfold(self):
  313.         selectedFaces = [face for face in self.src.faces if (self.src.faceUV and face.sel)]
  314.         if(self.avoidsOverlaps):
  315.             print "unfolding with overlap detection"
  316.         if(self.firstFaceIndex==None):
  317.             self.firstFaceIndex = random.randint(0, len(self.src.faces)-1)
  318.         else:
  319.             print "Using user-selected seed face ", self.firstFaceIndex
  320.         self.firstFace = self.src.faces[self.firstFaceIndex]
  321.         z = min([v.co.z for v in self.src.verts])-0.1
  322.         ff = Poly.fromBlenderFace(self.firstFace)
  323.         if(len(ff.v)<3):
  324.             raise Exception("This mesh contains an isolated edge - it must consist only of faces")
  325.         testFace = Poly.fromVectors( [ Vector([0.0,0.0,0.0]), Vector([0.0,1.0,0.0]), Vector([1.0,1.0,0.0])  ] )
  326.         # hmmm. I honestly can't remember why this needs to be done, but it does.
  327.         u=0
  328.         v=1
  329.         w=2
  330.         if ff.v[u].x==ff.v[u+1].x and ff.v[u].y==ff.v[u+1].y:
  331.             u=1
  332.             v=2
  333.             w=0
  334.         # here we make a couple of folds, not part of the net, which serve to get the net into the xy plane
  335.         xyFace = Poly.fromList( [ [ff.v[u].x,ff.v[u].y, z] , [ff.v[v].x,ff.v[v].y, z] , [ff.v[w].x+0.1,ff.v[w].y+0.1, z] ] )
  336.         refFace = Poly.fromVectors([ ff.v[u], ff.v[v], xyFace.v[1], xyFace.v[0] ] )
  337.         xyFold =  Fold(None,   xyFace, refFace, Edge(xyFace.v[0], xyFace.v[1] ))
  338.         self.refFold = Fold(xyFold, refFace, ff,         Edge(refFace.v[0], refFace.v[1] ))
  339.         self.refFold.srcFace = self.firstFace
  340.         # prepare to grow the trees
  341.         trunk = Tree(self, None, self.refFold)
  342.         trunk.generations = self.generations
  343.         self.firstPoly = ff
  344.         self.facesAndEdges.takeFace(self.firstFace)
  345.         self.myFacesVisited+=1
  346.         self.refFold.unfold()
  347.         self.refFold.tree = trunk
  348.         self.refFold.desFace = self.addFace(self.refFold.unfoldedFace(), self.refFold.srcFace)
  349.         self.folds.append(self.refFold)
  350.         trunk.grow()
  351.         i = 0
  352.         # keep the trees growing while they can
  353.         while(self.myFacesVisited<len(self.src.faces) and len(self.branches) > 0):
  354.             if self.edgeIteratorClass==RandomEdgeIterator:
  355.                 i = random.randint(0,len(self.branches)-1)
  356.             tree = self.branches[i]
  357.             if(tree.isGrown()):
  358.                 self.branches.pop(i)
  359.             else:
  360.                 tree.beGrowing()
  361.                 if(tree.canGrow()):
  362.                     tree.grow()
  363.                     i = 0
  364.                 else:
  365.                     i = (i + 1) % len(self.branches)
  366.         if self.src.faceUV:
  367.             for face in self.src.faces:
  368.                 face.sel = False
  369.             for face in selectedFaces:
  370.                 face.sel = True
  371.         self.src.update()
  372.         Window.RedrawAll()
  373.     def assignUVs(self):
  374.         for fold in self.folds:
  375.             self.assignUV(fold.srcFace, fold.unfoldedFace())
  376.         print " assigned uv to ", len(self.folds), len(self.src.faces)
  377.         self.src.update()
  378.     def checkOverlaps(self, fold):
  379.         #return self.getOverlapsBetween(fold, self.folds)
  380.         return self.getOverlapsBetweenGL(fold, self.folds)
  381.     def getOverlapsBetween(self, fold, folds):
  382.         if(fold.parent==None):
  383.             return []
  384.         mf = fold.unfoldedFace()
  385.         c = []
  386.         for afold in folds:
  387.             mdf = afold.unfoldedFace()
  388.             if(afold!=fold):
  389.                 # currently need to get agreement from both polys because
  390.                 # a touch by a vertex of one the other's edge is acceptable &
  391.                 # they disagree on that
  392.                 intersects = mf.intersects2D(mdf) and mdf.intersects2D(mf)
  393.                 inside = ( mdf.containsAnyOf(mf) or mf.containsAnyOf(mdf) )
  394.                 if(  intersects or inside or mdf.overlays(mf)):
  395.                     c.append(afold)
  396.         return c
  397.     def getOverlapsBetweenGL(self, fold, folds):
  398.         b = fold.unfoldedFace().bounds()
  399.         polys = len(folds)*4+16 # the buffer is nhits, mindepth, maxdepth, name
  400.         buffer = BGL.Buffer(BGL.GL_INT, polys)
  401.         BGL.glSelectBuffer(polys, buffer)
  402.         BGL.glRenderMode(BGL.GL_SELECT)
  403.         BGL.glInitNames()
  404.         BGL.glPushName(0)
  405.         BGL.glPushMatrix()
  406.         BGL.glMatrixMode(BGL.GL_PROJECTION)
  407.         BGL.glLoadIdentity()
  408.         BGL.glOrtho(b[0].x, b[1].x, b[1].y, b[0].y, 0.0, 10.0)
  409.         #clip = BGL.Buffer(BGL.GL_FLOAT, 4)
  410.         #clip.list = [0,0,0,0]
  411.         #BGL.glClipPlane(BGL.GL_CLIP_PLANE1, clip)
  412.         # could use clipping planes here too
  413.         BGL.glMatrixMode(BGL.GL_MODELVIEW)
  414.         BGL.glLoadIdentity()
  415.         bx = (b[1].x - b[0].x)
  416.         by = (b[1].y - b[0].y)
  417.         cx = bx / 2.0
  418.         cy = by / 2.0
  419.         for f in xrange(len(folds)):
  420.             afold = folds[f]
  421.             if(fold!=afold):
  422.                 BGL.glLoadName(f)
  423.                 BGL.glBegin(BGL.GL_LINE_LOOP)
  424.                 for v in afold.unfoldedFace().v:
  425.                     BGL.glVertex2f(v.x, v.y)
  426.                 BGL.glEnd()
  427.         BGL.glPopMatrix()
  428.         BGL.glFlush()
  429.         hits = BGL.glRenderMode(BGL.GL_RENDER)
  430.         buffer = [buffer[i] for i in xrange(3, 4*hits, 4)]
  431.         o = [folds[buffer[i]] for i in xrange(len(buffer))]
  432.         return self.getOverlapsBetween(fold, o)
  433.     def colourFace(self, face, cr):
  434.         for c in face.col:
  435.             c.r = int(cr[0])
  436.             c.g = int(cr[1])
  437.             c.b = int(cr[2])
  438.             c.a = int(cr[3])
  439.         self.src.update()
  440.     def setAvoidsOverlaps(self, avoids):
  441.         self.avoidsOverlaps = avoids
  442.     def addBranch(self, branch):
  443.         self.branches.append(branch)
  444.         if self.edgeIteratorClass!=RandomEdgeIterator:
  445.             self.branches.sort(lambda b1, b2: b1.compare(b2))
  446.     def srcSize(self):
  447.         return len(self.src.faces)
  448.     def nBranches(self):
  449.         return len(self.branches)
  450.     def facesCreated(self):
  451.         return len(self.des.faces)
  452.     def facesVisited(self):
  453.         return self.myFacesVisited
  454.     def getOverlaps(self):
  455.         return self.overlaps
  456.     def sortOutIPOSource(self):
  457.         print "Sorting out IPO"
  458.         if self.foldIPO!=None:
  459.             return
  460.         o = None
  461.         try:
  462.             o = Blender.Object.Get("FoldRate")
  463.         except:
  464.             o = Blender.Object.New("Empty", "FoldRate")
  465.             Blender.Scene.GetCurrent().objects.link(o)
  466.         if(o.getIpo()==None):
  467.             ipo = Blender.Ipo.New("Object", "FoldRateIPO")
  468.             z = ipo.addCurve("RotZ")
  469.             print " added RotZ IPO curve"
  470.             z.addBezier((1,0))
  471.             # again, why is this 10x out ?
  472.             z.addBezier((180, self.ff/10.0))
  473.             z.addBezier((361, 0.0))
  474.             o.setIpo(ipo)
  475.             z.recalc()
  476.             z.setInterpolation("Bezier")
  477.             z.setExtrapolation("Cyclic")
  478.         self.setIPOSource(o)
  479.         print " added IPO source"
  480.     def setIPOSource(self, object):
  481.         try:
  482.             self.foldIPO = object
  483.             for i in xrange(self.foldIPO.getIpo().getNcurves()):
  484.                 self.IPOCurves[self.foldIPO.getIpo().getCurves()[i].getName()] = i
  485.                 print " added ", self.foldIPO.getIpo().getCurves()[i].getName()
  486.         except:
  487.             print "Problem setting IPO object"
  488.             print sys.exc_info()[1]
  489.             traceback.print_exc(file=sys.stdout)
  490.     def setFoldFactor(self, ff):
  491.         self.ff = ff
  492.     def sayTree(self):
  493.         for fold in self.folds:
  494.             if(fold.getParent()!=None):
  495.                 print fold.getID(), fold.dihedralAngle(), fold.getParent().getID()
  496.     def report(self):
  497.         p = int(float(self.myFacesVisited)/float(len(self.src.faces)) * 100)
  498.         print str(p) + "% unfolded"
  499.         print "faces created:", self.facesCreated()
  500.         print "faces visited:", self.facesVisited()
  501.         print "originalfaces:", len(self.src.faces)
  502.         n=0
  503.         if(self.avoidsOverlaps):
  504.             print "net avoided at least ", self.getOverlaps(), " overlaps ",
  505.             n = len(self.src.faces) - self.facesCreated()
  506.             if(n>0):
  507.                 print "but was unable to avoid ", n, " overlaps. Incomplete net."
  508.             else:
  509.                 print "- A complete net."
  510.         else:
  511.             print "net has at least ", self.getOverlaps(), " collision(s)"
  512.         return n
  513.     # fold all my folds to a fraction of their total fold angle
  514.     def unfoldToCurrentFrame(self):
  515.         self.unfoldTo(Blender.Scene.GetCurrent().getRenderingContext().currentFrame())
  516.     def unfoldTo(self, frame):
  517.         frames = Blender.Scene.GetCurrent().getRenderingContext().endFrame()
  518.         if(self.foldIPO!=None and self.foldIPO.getIpo()!=None):
  519.             f = self.foldIPO.getIpo().EvaluateCurveOn(self.IPOCurves["RotZ"],frame)
  520.             # err, this number seems to be 10x less than it ought to be
  521.             fff = 1.0 - (f*10.0 / self.ff)
  522.         else:
  523.             fff = 1.0-((frame)/(frames*1.0))
  524.         for fold in self.folds:
  525.             fold.unfoldTo(fff)
  526.         for fold in self.folds:
  527.             fold.unfold()
  528.             tface = fold.unfoldedFace()
  529.             bface = fold.desFace
  530.             i = 0
  531.             for v in bface.verts:
  532.                 v.co.x = tface.v[i].x
  533.                 v.co.y = tface.v[i].y
  534.                 v.co.z = tface.v[i].z
  535.                 i+=1
  536.         Window.Redraw(Window.Types.VIEW3D)
  537.         return None
  538.     def addFace(self, poly, originalFace=None):
  539.         originalLength = len(self.des.verts)
  540.         self.des.verts.extend([Vector(vv.x, vv.y, vv.z) for vv in poly.v])
  541.         self.des.faces.extend([ range(originalLength, originalLength + poly.size()) ])
  542.         newFace = self.des.faces[len(self.des.faces)-1]
  543.         newFace.uv = [vv for vv in poly.v]
  544.         if(originalFace!=None and self.src.vertexColors):
  545.             newFace.col = [c for c in originalFace.col]
  546.         if(self.feedback!=None):
  547.             pu = str(int(self.fractionUnfolded() * 100))+"% unfolded"
  548.             howMuchDone = str(self.myFacesVisited)+" of "+str(len(self.src.faces))+"  "+pu
  549.             self.feedback.say(howMuchDone)
  550.             #Window.DrawProgressBar (p, pu)
  551.         if(self.showProgress):
  552.             Window.Redraw(Window.Types.VIEW3D)
  553.         return newFace
  554.     def fractionUnfolded(self):
  555.         return float(self.myFacesVisited)/float(len(self.src.faces))
  556.     def assignUV(self, face, uv):
  557.         face.uv = [Vector(v.x, v.y) for v in uv.v]
  558.     def unfoldAll(feedback=None):
  559.         objects = Blender.Object.Get()
  560.         for object in objects:
  561.             if(object.getType()=='Mesh' and not(object.getName().endswith("_net")) and len(object.getData(False, True).faces)>1):
  562.                 net = Net.createNet(object, feedback)
  563.                 net.searchForUnfolding()
  564.                 svg = SVGExporter(net, object.getName()+".svg")
  565.                 svg.export()
  566.     unfoldAll = staticmethod(unfoldAll)
  567.     def searchForUnfolding(self, limit=-1):
  568.         overlaps = 1
  569.         attempts = 0
  570.         while(overlaps > 0 or attempts<limit):
  571.             self.unfold()
  572.             overlaps = self.report()
  573.             attempts+=1
  574.         return attempts
  575.     def fromSelected(feedback=None, netName=None):
  576.         return Net.createNet(Blender.Object.GetSelected()[0], feedback, netName)
  577.     fromSelected = staticmethod(fromSelected)
  578.     def clone(self, object=None):
  579.         if(object==None):
  580.             object = self.object
  581.         net = Net.createNet(object, self.feedback)
  582.         net.avoidsOverlaps = net.avoidsOverlaps
  583.         return net
  584.     def createNet(ob, feedback=None, netName=None):
  585.         mesh = ob.getData(mesh=1)
  586.         netObject = None
  587.         if(netName==None):
  588.             netName = ob.name[0:16]+"_net"
  589.         try:
  590.             netObject = Blender.Object.Get(netName)
  591.             netMesh = netObject.getData(False, True)
  592.             if(netMesh!=None):
  593.                 netMesh.verts = None # clear the mesh
  594.             else:
  595.                 netObject = Blender.Object.New("Mesh", netName)
  596.         except:
  597.             if(netObject==None):
  598.                 netObject = Blender.Object.New("Mesh", netName)
  599.         netMesh = netObject.getData(False, True)  # True means "as a Mesh not an NMesh"
  600.         try:
  601.             Blender.Scene.GetCurrent().objects.link(netObject)
  602.         except:
  603.             pass
  604.         try:
  605.             netMesh.materials = mesh.materials
  606.             netMesh.vertexColors = True
  607.         except:
  608.             print "Problem setting materials here"
  609.         net = Net(mesh, netMesh)
  610.         if mesh.faceUV and mesh.activeFace>=0 and (mesh.faces[mesh.activeFace].sel):
  611.             net.firstFaceIndex = mesh.activeFace
  612.         net.object = ob
  613.         net.feedback = feedback
  614.         return net
  615.     createNet = staticmethod(createNet)
  616.     def importNet(filename):
  617.         netName = filename.rstrip(".svg").replace("\\","/")
  618.         netName = netName[netName.rfind("/")+1:]
  619.         try:
  620.             netObject = Blender.Object.Get(netName)
  621.         except:
  622.             netObject  = Blender.Object.New("Mesh", netName)
  623.         netObject.getData(mesh=1).name = netName
  624.         try:
  625.             Blender.Scene.GetCurrent().objects.link(netObject)
  626.         except:
  627.             pass
  628.         net = Net(None, netObject.getData(mesh=1))
  629.         handler = NetHandler(net)
  630.         xml.sax.parse(filename, handler)
  631.         Window.Redraw(Window.Types.VIEW3D)
  632.         return net
  633.     importNet = staticmethod(importNet)
  634.     def getSourceMesh(self):
  635.         return self.src
  636.         
  637. # determines the order in which to visit faces according to a local measure        
  638. class EdgeIterator:
  639.     def __init__(self, branch, otherConstructor=None):
  640.         self.branch = branch
  641.         self.bface = branch.getFace()
  642.         self.edge = branch.getFold().getEdge()
  643.         self.net = branch.getNet()
  644.         self.n = len(self.bface)
  645.         self.edges = []
  646.         self.i = 0
  647.         self.gooodness = 0
  648.         self.createEdges()
  649.         self.computeGoodness()
  650.         if(otherConstructor==None):
  651.             self.sequenceEdges()
  652.     def createEdges(self):
  653.         edge = None
  654.         e = Edge.edgesOfBlenderFace(self.net.getSourceMesh(), self.bface)
  655.         for edge in e:
  656.             if not(edge.isBlenderSeam() and edge!=self.edge):
  657.                 self.edges.append(edge)
  658.     def sequenceEdges(self):
  659.         pass
  660.     def next(self):
  661.         edge = self.edges[self.i]
  662.         self.i+=1
  663.         return edge
  664.     def size(self):
  665.         return len(self.edges)
  666.     def reset(self):
  667.         self.i = 0
  668.     def hasNext(self):
  669.         return (self.i<len(self.edges))
  670.     def goodness(self):
  671.         return self.gooodness
  672.     def computeGoodness(self):
  673.         self.gooodness = 0
  674.     def rotate(self):
  675.         self.edges.append(self.edges.pop(0))
  676.  
  677. class RandomEdgeIterator(EdgeIterator):
  678.     def sequenceEdges(self):
  679.         random.seed()
  680.         random.shuffle(self.edges)
  681.     def goodness(self):
  682.         return random.randint(0, self.net.srcSize())
  683.     
  684.         
  685. class Largest(EdgeIterator):
  686.     def sequenceEdges(self):
  687.         for e in self.edges:
  688.             f = self.net.facesAndEdges.findAdjacentFace(self.bface, e)
  689.             if(f!=None):
  690.                 e.setGoodness(f.area)
  691.         self.edges.sort(lambda e1, e2: -e1.compare(e2))
  692.     def computeGoodness(self):
  693.         self.gooodness = self.bface.area
  694.         
  695.             
  696. class Brightest(EdgeIterator):
  697.     def sequenceEdges(self):
  698.         for edge in self.edges:
  699.             f = self.net.facesAndEdges.findAdjacentFace(self.bface, edge)
  700.             if(f!=None):
  701.                 b = 0
  702.                 if self.net.src.vertexColors:
  703.                     for c in f.col:
  704.                         b+=(c.g+c.r+c.b)
  705.                 rc = float(random.randint(0, self.net.srcSize())) / float(self.net.srcSize()) / 100.0
  706.                 b+=rc
  707.                 edge.setGoodness(b)
  708.         self.edges.sort(lambda e1, e2: e1.compare(e2))
  709.     def computeGoodness(self):
  710.         g = 0
  711.         if self.net.src.vertexColors:
  712.             for c in self.bface.col:
  713.                 g+=(c.g+c.r+c.b)
  714.         self.gooodness = g
  715.         
  716. class OddEven(EdgeIterator):
  717.     i = True
  718.     def sequenceEdges(self):
  719.         OddEven.i = not(OddEven.i)
  720.         if(OddEven.i):
  721.             self.edges.reverse()
  722.         
  723. class Curvature(EdgeIterator):
  724.     def sequenceEdges(self):
  725.         p1 = Poly.fromBlenderFace(self.bface)
  726.         gg = 0.0
  727.         for edge in self.edges:
  728.             f = self.net.facesAndEdges.findAdjacentFace(self.bface, edge)
  729.             if(f!=None):
  730.                 p2 = Poly.fromBlenderFace(f)
  731.                 fold = Fold(None, p1, p2, edge)
  732.                 fold.srcFace = f
  733.                 b = Tree(self.net, self.branch, fold, self)
  734.                 c = Curvature(b, False)
  735.                 g = c.goodness()
  736.                 gg+=g
  737.                 edge.setGoodness(g)
  738.         self.edges.sort(lambda e1, e2: e1.compare(e2))
  739.         tg = (self.gooodness + gg)
  740.         rc = float(random.randint(0, self.net.srcSize())) / float(self.net.srcSize()) / 100.0
  741.         if(tg!=0.0):
  742.             self.gooodness = self.gooodness + rc / tg
  743.     def computeGoodness(self):
  744.         g = 0
  745.         for edge in self.edges:
  746.             f = self.net.facesAndEdges.findAdjacentFace(self.bface, edge)
  747.             if(f!=None):
  748.                 p1 = Poly.fromBlenderFace(self.bface)
  749.                 p2 = Poly.fromBlenderFace(f)
  750.                 f = Fold(None, p1, p2, edge)
  751.                 g += f.dihedralAngle()
  752.         self.gooodness = g
  753.         
  754.  
  755. class Edge:
  756.     def __init__(self, v1=None, v2=None, mEdge=None, i=-1):
  757.         self.idx = i
  758.         if v1 and v2:
  759.             self.v1 = v1.copy()
  760.             self.v2 = v2.copy()
  761.         else:
  762.             self.v1 = mEdge.v1.co.copy()
  763.             self.v2 = mEdge.v2.co.copy()
  764.         self.v1n = -self.v1
  765.         self.vector = self.v1-self.v2
  766.         self.vector.resize3D()
  767.         self.vector.normalize()
  768.         self.bmEdge = mEdge
  769.         self.gooodness = 0.0
  770.     def fromBlenderFace(mesh, bface, i):
  771.         if(i>len(bface)-1):
  772.             return None
  773.         if(i==len(bface)-1):
  774.             j = 0
  775.         else:
  776.             j = i+1
  777.         edge =  Edge( bface.v[i].co.copy(), bface.v[j].co.copy() )
  778.         edge.bEdge = mesh.findEdge(bface.v[i], bface.v[j])
  779.         edge.idx = i
  780.         return edge
  781.     fromBlenderFace=staticmethod(fromBlenderFace)
  782.     def edgesOfBlenderFace(mesh, bmFace):
  783.         edges = [mesh.edges[mesh.findEdges(edge[0], edge[1])] for edge in bmFace.edge_keys]
  784.         v = bmFace.verts
  785.         e = []
  786.         vi = v[0]
  787.         i=0
  788.         for j in xrange(1, len(bmFace)+1):
  789.             vj = v[j%len(bmFace)]
  790.             for ee in edges:
  791.                 if((ee.v1.index==vi.index and ee.v2.index==vj.index) or (ee.v2.index==vi.index and ee.v1.index==vj.index)):
  792.                     e.append(Edge(vi.co, vj.co, ee, i))
  793.                     i+=1
  794.             vi = vj
  795.         return e
  796.     edgesOfBlenderFace=staticmethod(edgesOfBlenderFace)
  797.     def isBlenderSeam(self):
  798.         return (self.bmEdge.flag & Mesh.EdgeFlags.SEAM)
  799.     def isInFGon(self):
  800.         return (self.bmEdge.flag & Mesh.EdgeFlags.FGON)
  801.     def mapTo(self, poly):
  802.         if(self.idx==len(poly.v)-1):
  803.             j = 0
  804.         else:
  805.             j = self.idx+1
  806.         return Edge(poly.v[self.idx], poly.v[j])
  807.     def isDegenerate(self):
  808.         return self.vector.length==0
  809.     def vertices(s):
  810.         return [ [s.v1.x, s.v1.y, s.v1.z], [s.v2.x, s.v2.y,s.v2.z] ]
  811.     def key(self):
  812.         return self.bmEdge.key
  813.     def goodness(self):
  814.         return self.gooodness
  815.     def setGoodness(self, g):
  816.         self.gooodness = g
  817.     def compare(self, other):
  818.         if(self.goodness() > other.goodness()):
  819.             return +1
  820.         else:
  821.             return -1
  822.     # Does the given segment intersect this, for overlap detection.
  823.     # endpoints are allowed to touch the line segment
  824.     def intersects2D(self, s):
  825.         if(self.matches(s)):
  826.             return False
  827.         else:
  828.             i = Geometry.LineIntersect2D(self.v1, self.v2, s.v1, s.v2)
  829.             if(i!=None):
  830.                 i.resize4D()
  831.                 i.z = self.v1.z # hack to put the point on the same plane as this edge for comparison
  832.             return(i!=None and not(self.endsWith(i)))
  833.     def matches(self, s):
  834.         return ( (self.v1==s.v1 and self.v2==s.v2) or (self.v2==s.v1 and self.v1==s.v2) )
  835.     # Is the given point on the end of this segment ? 10-5 seems to an acceptable limit for closeness in Blender
  836.     def endsWith(self, aPoint, e=0.0001):
  837.         return ( (self.v1-aPoint).length < e or (self.v2-aPoint).length < e )
  838.  
  839.     
  840. class Poly:
  841.     ids = -1
  842.     def __init__(self):
  843.         Poly.ids+=1
  844.         self.v = []
  845.         self.id = Poly.ids
  846.         self.boundz = None
  847.         self.edges = None
  848.     def getID(self):
  849.         return self.id
  850.     def normal(self):
  851.         a =self.v[0]
  852.         b=self.v[1]
  853.         c=self.v[2]
  854.         p = b-a
  855.         p.resize3D()
  856.         q = a-c
  857.         q.resize3D()
  858.         return p.cross(q)
  859.     def makeEdges(self):
  860.         self.edges = []
  861.         for i in xrange(self.nPoints()):
  862.             self.edges.append(Edge( self.v[i % self.nPoints()], self.v[ (i+1) % self.nPoints()] ))
  863.     def edgeAt(self, i):
  864.         if(self.edges==None):
  865.             self.makeEdges()
  866.         return self.edges[i]
  867.     def intersects2D(self, poly):
  868.         for i in xrange(self.nPoints()):
  869.             edge = self.edgeAt(i)
  870.             for j in xrange(poly.nPoints()):
  871.                 if edge.intersects2D(poly.edgeAt(j)):
  872.                     return True
  873.         return False
  874.     def isBad(self):
  875.         badness = 0
  876.         for vv in self.v:
  877.             if(vv.x!=vv.x or vv.y!=vv.y or vv.z!=vv.z): # Nan check
  878.                 badness+=1
  879.         return (badness>0)
  880.     def midpoint(self):
  881.         x=y=z = 0.0
  882.         n = 0
  883.         for vv in self.v:
  884.             x+=vv.x
  885.             y+=vv.y
  886.             z+=vv.z
  887.             n+=1
  888.         return [ x/n, y/n, z/n ]
  889.     def centerAtOrigin(self):
  890.         mp = self.midpoint()
  891.         mp = -mp
  892.         toOrigin = TranslationMatrix(mp)
  893.         self.v = [(vv * toOrigin) for vv in self.v]
  894.     def move(self, tv):
  895.         mv = TranslationMatrix(tv)
  896.         self.v = [(vv * mv) for vv in self.v]
  897.     def scale(self, s):
  898.         mp = Vector(self.midpoint())
  899.         fromOrigin = TranslationMatrix(mp)
  900.         mp = -mp
  901.         toOrigin = TranslationMatrix(mp)
  902.         sm = ScaleMatrix(s, 4)
  903.         # Todo, the 3 lines below in 1 LC
  904.         self.v = [(vv * toOrigin) for vv in self.v]
  905.         self.v = [(sm * vv) for vv in self.v]
  906.         self.v = [(vv * fromOrigin) for vv in self.v]
  907.     def nPoints(self):
  908.         return len(self.v)
  909.     def size(self):
  910.         return len(self.v)
  911.     def rotated(self, axis, angle):
  912.         p = self.clone()
  913.         p.rotate(axis, angle)
  914.         return p
  915.     def rotate(self, axis, angle):
  916.         rotation = RotationMatrix(angle, 4, "r", axis.vector)
  917.         toOrigin = TranslationMatrix(axis.v1n)
  918.         fromOrigin = TranslationMatrix(axis.v1)
  919.         # Todo, the 3 lines below in 1 LC
  920.         self.v = [(vv * toOrigin) for vv in self.v]
  921.         self.v = [(rotation * vv) for vv in self.v]
  922.         self.v = [(vv * fromOrigin) for vv in self.v]
  923.     def moveAlong(self, vector, distance):
  924.         t = TranslationMatrix(vector)
  925.         s = ScaleMatrix(distance, 4)
  926.         ts = t*s
  927.         self.v = [(vv * ts) for vv in self.v]
  928.     def bounds(self):
  929.         if(self.boundz == None):
  930.             vv = [vv for vv in self.v]
  931.             vv.sort(key=lambda v: v.x)
  932.             minx = vv[0].x
  933.             maxx = vv[len(vv)-1].x
  934.             vv.sort(key=lambda v: v.y)
  935.             miny = vv[0].y
  936.             maxy = vv[len(vv)-1].y
  937.             self.boundz = [Vector(minx, miny, 0), Vector(maxx, maxy, 0)]
  938.         return self.boundz
  939.     def fromBlenderFace(bface):
  940.         p = Poly()
  941.         for vv in bface.v:
  942.             vec = Vector([vv.co[0], vv.co[1], vv.co[2] , 1.0]) 
  943.             p.v.append(vec)
  944.         return p
  945.     fromBlenderFace = staticmethod(fromBlenderFace)
  946.     def fromList(list):
  947.         p = Poly()
  948.         for vv in list:
  949.             vec = Vector( [vvv for vvv in vv] )
  950.             vec.resize4D()
  951.             p.v.append(vec)
  952.         return p
  953.     fromList = staticmethod(fromList)
  954.     def fromVectors(vectors):
  955.         p = Poly()
  956.         p.v.extend([v.copy().resize4D() for v in vectors])
  957.         return p
  958.     fromVectors = staticmethod(fromVectors)
  959.     def clone(self):
  960.         p = Poly()
  961.         p.v.extend(self.v)
  962.         return p
  963.     def hasVertex(self, ttv):
  964.         v = Mathutils.Vector(ttv)
  965.         v.normalize()
  966.         for tv in self.v:
  967.             vv = Mathutils.Vector(tv)
  968.             vv.normalize()
  969.             t = 0.00001
  970.             if abs(vv.x-v.x)<t and abs(vv.y-v.y)<t:
  971.                 return True
  972.         return False
  973.     def overlays(self, poly):
  974.         if len(poly.v)!=len(self.v):
  975.             return False
  976.         c = 0
  977.         for point in poly.v:
  978.             if self.hasVertex(point):
  979.                 c+=1
  980.         return c==len(self.v)
  981.     def sharesVertexWith(self, poly):
  982.         for point in poly.v:
  983.             if(self.hasVertex(point)):
  984.                 return True
  985.         return False
  986.     def containsAnyOf(self, poly):
  987.         for point in poly.v:
  988.             if(not(self.hasVertex(point))):
  989.                 if self.contains(point):
  990.                     return True
  991.         return False
  992.     def toString(self):
  993.         return self.v
  994.     # This is the BEST algorithm for point-in-polygon detection.
  995.     # It's by W. Randolph Franklin.
  996.     # returns 1 for inside, 1 or 0 for edges
  997.     def contains(self, tp):
  998.         c = 0
  999.         j = len(self.v)-1
  1000.         for i in xrange(len(self.v)):
  1001.             if(i>0): j=i-1
  1002.             cv = self.v[i]
  1003.             nv = self.v[j]
  1004.             if ((((cv.y<=tp.y) and (tp.y<nv.y)) or ((nv.y<=tp.y) and (tp.y<cv.y))) and (tp.x < (nv.x - cv.x) * (tp.y - cv.y) / (nv.y - cv.y) + cv.x)):
  1005.                 c = not(c)
  1006.         return (c == 1)
  1007.         
  1008. class SVGExporter:
  1009.     def __init__(self, net, filename):
  1010.         self.net = net
  1011.         print self.net.des.name
  1012.         self.object = self.net.object
  1013.         print "Exporting ", self.object
  1014.         self.filename = filename
  1015.         self.file = None
  1016.         self.e = None
  1017.         self.width = 1024
  1018.         self.height = 768
  1019.     def start(self):
  1020.         print "Exporting SVG to ", self.filename
  1021.         self.file = open(self.filename, 'w')
  1022.         self.e = xml.sax.saxutils.XMLGenerator(self.file, "UTF-8")
  1023.         atts = {}
  1024.         atts["width"] = "100%"
  1025.         atts["height"] = "100%"
  1026.         atts["viewBox"] = str(self.vxmin)+" "+str(self.vymin)+" "+str(self.vxmax-self.vxmin)+" "+str(self.vymax-self.vymin)
  1027.         atts["xmlns:nets"] = "http://celeriac.net/unfolder/rdf#"
  1028.         atts["xmlns:xlink"] = "http://www.w3.org/1999/xlink"
  1029.         atts["xmlns"] ="http://www.w3.org/2000/svg"
  1030.         a = xml.sax.xmlreader.AttributesImpl(atts)
  1031.         self.e.startDocument()
  1032.         self.e.startElement("svg", a)
  1033.         self.e.startElement("defs", xml.sax.xmlreader.AttributesImpl({}))
  1034.         atts = {}
  1035.         atts["type"]="text/css"
  1036.         self.e.startElement("style", atts)
  1037.         # can't find a proper way to do this
  1038.         self.file.write("<![CDATA[")
  1039.         self.file.write("polygon.poly{fill:white;stroke:black;stroke-width: 0.001}")
  1040.         self.file.write("g#foldLines line.valley{stroke:white;stroke-width:0.01;stroke-dasharray:0.02,0.01,0.02,0.05}")
  1041.         self.file.write("g#foldLines line.mountain{stroke:white;stroke-width:0.01;stroke-dasharray:0.02,0.04}")
  1042.         self.file.write("]]>")
  1043.         self.e.endElement("style")
  1044.         self.e.endElement("defs")
  1045.         #self.addClipPath()
  1046.         self.addMeta()
  1047.     def addMeta(self):
  1048.         self.e.startElement("metadata", xml.sax.xmlreader.AttributesImpl({}))
  1049.         self.e.startElement("nets:net", xml.sax.xmlreader.AttributesImpl({}))
  1050.         for i in xrange(1, len(self.net.folds)):
  1051.             fold = self.net.folds[i]
  1052.             # AttributesNSImpl - documentation is rubbish. using this hack.
  1053.             atts = {}
  1054.             atts["nets:id"] = "fold"+str(fold.getID())
  1055.             if(fold.parent!=None):
  1056.                 atts["nets:parent"] = "fold"+str(fold.parent.getID())
  1057.             else:
  1058.                 atts["nets:parent"] = "null"
  1059.             atts["nets:da"] = str(fold.dihedralAngle())
  1060.             if(fold.parent!=None):
  1061.                 atts["nets:ofPoly"] = "poly"+str(fold.parent.foldingPoly.getID())
  1062.             else:
  1063.                 atts["nets:ofPoly"] = ""
  1064.             atts["nets:toPoly"] = "poly"+str(fold.foldingPoly.getID())
  1065.             a = xml.sax.xmlreader.AttributesImpl(atts)
  1066.             self.e.startElement("nets:fold",  a)
  1067.             self.e.endElement("nets:fold")
  1068.         self.e.endElement("nets:net")
  1069.         self.e.endElement("metadata")
  1070.     def end(self):
  1071.         self.e.endElement("svg")
  1072.         self.e.endDocument()
  1073.         print "grown."
  1074.     def export(self):
  1075.         self.net.unfoldTo(1)
  1076.         bb = self.object.getBoundBox()
  1077.         print bb
  1078.         self.vxmin = bb[0][0]
  1079.         self.vymin = bb[0][1]
  1080.         self.vxmax = bb[7][0]
  1081.         self.vymax = bb[7][1]
  1082.         self.start()
  1083.         atts = {}
  1084.         atts["id"] = self.object.getName()
  1085.         a = xml.sax.xmlreader.AttributesImpl(atts)
  1086.         self.e.startElement("g", a)
  1087.         #self.addUVImage()
  1088.         self.addPolys()
  1089.         self.addFoldLines()
  1090.         #self.addCutLines()
  1091.         self.e.endElement("g")
  1092.         self.end()
  1093.     def addClipPath(self):
  1094.         atts = {}
  1095.         atts["id"] = "netClip"
  1096.         atts["clipPathUnits"] = "userSpaceOnUse"
  1097.         atts["x"] = str(self.vxmin)
  1098.         atts["y"] = str(self.vymin)
  1099.         atts["width"] = "100%"
  1100.         atts["height"] = "100%"
  1101.         self.e.startElement("clipPath", atts)
  1102.         self.addPolys()
  1103.         self.e.endElement("clipPath")
  1104.     def addUVImage(self):
  1105.         image = Blender.Image.GetCurrent() #hmm - how to determine the desired image ?
  1106.         if image==None:
  1107.             return
  1108.         ifn = image.getFilename()
  1109.         ifn = self.filename.replace(".svg", ".jpg")
  1110.         image.setFilename(ifn)
  1111.         ifn = ifn[ifn.rfind("/")+1:]
  1112.         image.save()
  1113.         atts = {}
  1114.         atts["clip-path"] = "url(#netClip)"
  1115.         atts["xlink:href"] = ifn
  1116.         self.e.startElement("image", atts)
  1117.         self.e.endElement("image")
  1118.     def addPolys(self):
  1119.         atts = {}
  1120.         atts["id"] = "polys"
  1121.         a = xml.sax.xmlreader.AttributesImpl(atts)
  1122.         self.e.startElement("g", a)
  1123.         for i in xrange(len(self.net.folds)):
  1124.             self.addPoly(self.net.folds[i])
  1125.         self.e.endElement("g")
  1126.     def addFoldLines(self):
  1127.         atts = {}
  1128.         atts["id"] = "foldLines"
  1129.         a = xml.sax.xmlreader.AttributesImpl(atts)
  1130.         self.e.startElement("g", a)
  1131.         for i in xrange( 1, len(self.net.folds)):
  1132.             self.addFoldLine(self.net.folds[i])
  1133.         self.e.endElement("g")
  1134.     def addFoldLine(self, fold):
  1135.         edge = fold.edge.mapTo(fold.parent.foldingPoly)
  1136.         if fold.dihedralAngle()>0:
  1137.             foldType="valley"
  1138.         else:
  1139.             foldType="mountain"
  1140.         atts={}
  1141.         atts["x1"] = str(edge.v1.x)
  1142.         atts["y1"] = str(edge.v1.y)
  1143.         atts["x2"] = str(edge.v2.x)
  1144.         atts["y2"] = str(edge.v2.y)
  1145.         atts["id"] = "fold"+str(fold.getID())
  1146.         atts["class"] = foldType
  1147.         a = xml.sax.xmlreader.AttributesImpl(atts)
  1148.         self.e.startElement("line", a)
  1149.         self.e.endElement("line")
  1150.     def addCutLines(self):
  1151.         atts = {}
  1152.         atts["id"] = "cutLines"
  1153.         a = xml.sax.xmlreader.AttributesImpl(atts)
  1154.         self.e.startElement("g", a)
  1155.         for i in xrange( 1, len(self.net.cuts)):
  1156.             self.addCutLine(self.net.cuts[i])
  1157.         self.e.endElement("g")
  1158.     def addCutLine(self, cut):
  1159.         edge = cut.edge.mapTo(cut.parent.foldingPoly)
  1160.         if cut.dihedralAngle()>0:
  1161.             foldType="valley"
  1162.         else:
  1163.             foldType="mountain"
  1164.         atts={}
  1165.         atts["x1"] = str(edge.v1.x)
  1166.         atts["y1"] = str(edge.v1.y)
  1167.         atts["x2"] = str(edge.v2.x)
  1168.         atts["y2"] = str(edge.v2.y)
  1169.         atts["id"] = "cut"+str(cut.getID())
  1170.         atts["class"] = foldType
  1171.         a = xml.sax.xmlreader.AttributesImpl(atts)
  1172.         self.e.startElement("line", a)
  1173.         self.e.endElement("line")
  1174.     def addPoly(self, fold):
  1175.         face = fold.foldingPoly
  1176.         atts = {}
  1177.         if fold.desFace.col:
  1178.             col = fold.desFace.col[0]
  1179.             rgb = "rgb("+str(col.r)+","+str(col.g)+","+str(col.b)+")"
  1180.             atts["fill"] = rgb
  1181.         atts["class"] = "poly"
  1182.         atts["id"] = "poly"+str(face.getID())
  1183.         points = ""
  1184.         first = True
  1185.         for vv in face.v:
  1186.             if(not(first)):
  1187.                 points+=','
  1188.             first = False
  1189.             points+=str(vv[0])
  1190.             points+=' '
  1191.             points+=str(vv[1])
  1192.         atts["points"] = points
  1193.         a = xml.sax.xmlreader.AttributesImpl(atts)
  1194.         self.e.startElement("polygon", a)
  1195.         self.e.endElement("polygon")
  1196.     def fileSelected(filename):
  1197.         try:
  1198.             net = Registry.GetKey('unfolder')['net']
  1199.             exporter = SVGExporter(net, filename)
  1200.             exporter.export()
  1201.         except:
  1202.             print "Problem exporting SVG"
  1203.             traceback.print_exc(file=sys.stdout)
  1204.     fileSelected = staticmethod(fileSelected)    
  1205.  
  1206. # for importing nets saved by the above exporter
  1207. class NetHandler(xml.sax.handler.ContentHandler):
  1208.     def __init__(self, net):
  1209.         self.net = net
  1210.         self.first = (41==41)
  1211.         self.currentElement = None
  1212.         self.chars = None
  1213.         self.currentAction = None
  1214.         self.foldsPending = {}
  1215.         self.polys = {}
  1216.         self.actions = {}
  1217.         self.actions["nets:fold"] = self.foldInfo
  1218.         self.actions["line"] = self.cutOrFold
  1219.         self.actions["polygon"] = self.createPoly
  1220.     def setDocumentLocator(self, locator):
  1221.         pass
  1222.     def startDocument(self):
  1223.         pass
  1224.     def endDocument(self):
  1225.         for fold in self.foldsPending.values():
  1226.             face = self.net.addFace(fold.unfoldedFace())
  1227.             fold.desFace = face
  1228.             self.net.folds.append(fold)
  1229.         self.net.addFace(self.first)
  1230.         self.foldsPending = None
  1231.         self.polys = None
  1232.     def startPrefixMapping(self, prefix, uri):
  1233.         pass
  1234.     def endPrefixMapping(self, prefix):
  1235.         pass
  1236.     def startElement(self, name, attributes):
  1237.         self.currentAction = None
  1238.         try:
  1239.             self.currentAction = self.actions[name]
  1240.         except:
  1241.             pass
  1242.         if(self.currentAction!=None):
  1243.             self.currentAction(attributes)
  1244.     def endElement(self, name):
  1245.         pass
  1246.     def startElementNS(self, name, qname, attrs):
  1247.         self.currentAction = self.actions[name]
  1248.         if(self.currentAction!=None):
  1249.             self.currentAction(attributes)
  1250.     def endElementNS(self, name, qname):
  1251.         pass
  1252.     def characters(self, content):
  1253.         pass
  1254.     def ignorableWhitespace(self):
  1255.         pass
  1256.     def processingInstruction(self, target, data):
  1257.         pass
  1258.     def skippedEntity(self, name):
  1259.         pass
  1260.     def foldInfo(self, atts):
  1261.         self.foldsPending[atts["nets:id"]] = atts
  1262.     def createPoly(self, atts):
  1263.         xy = re.split('[, ]' , atts["points"])
  1264.         vectors = []
  1265.         for i in xrange(0, len(xy)-1, 2):
  1266.             v = Vector([float(xy[i]), float(xy[i+1]), 0.0])
  1267.             vectors.append(v)
  1268.         poly = Poly.fromVectors(vectors)
  1269.         if(self.first==True):
  1270.             self.first = poly
  1271.         self.polys[atts["id"]] = poly
  1272.     def cutOrFold(self, atts):
  1273.         fid = atts["id"]
  1274.         try:
  1275.             fi = self.foldsPending[fid]
  1276.         except:
  1277.             pass
  1278.         p1 = Vector([float(atts["x1"]), float(atts["y1"]), 0.0])
  1279.         p2 = Vector([float(atts["x2"]), float(atts["y2"]), 0.0])
  1280.         edge = Edge(p1, p2)
  1281.         parent = None
  1282.         ofPoly = None
  1283.         toPoly = None
  1284.         try: 
  1285.             parent = self.foldsPending[fi["nets:parent"]]
  1286.         except:
  1287.             pass
  1288.         try:
  1289.             ofPoly = self.polys[fi["nets:ofPoly"]]
  1290.         except:
  1291.             pass
  1292.         try:
  1293.             toPoly = self.polys[fi["nets:toPoly"]]
  1294.         except:
  1295.             pass
  1296.         fold = Fold(parent, ofPoly , toPoly, edge, float(fi["nets:da"]))
  1297.         self.foldsPending[fid] = fold
  1298.     def fileSelected(filename):
  1299.         try:
  1300.             net = Net.importNet(filename)
  1301.             try:
  1302.                 Registry.GetKey('unfolder')['net'] = net
  1303.             except:
  1304.                 Registry.SetKey('unfolder', {})
  1305.                 Registry.GetKey('unfolder')['net'] = net
  1306.             Registry.GetKey('unfolder')['lastpath'] = filename
  1307.         except:
  1308.             print "Problem importing SVG"
  1309.             traceback.print_exc(file=sys.stdout)
  1310.     fileSelected = staticmethod(fileSelected)        
  1311.  
  1312.  
  1313. class GUI:
  1314.     def __init__(self):
  1315.         self.overlaps = Draw.Create(0)
  1316.         self.ani = Draw.Create(0)
  1317.         self.selectedFaces =0
  1318.         self.search = Draw.Create(0)
  1319.         self.diffuse = True
  1320.         self.ancestors = Draw.Create(0)
  1321.         self.noise = Draw.Create(0.0)
  1322.         self.shape = Draw.Create(0)
  1323.         self.nOverlaps = 1==2
  1324.         self.iterators = [RandomEdgeIterator,Brightest,Curvature,EdgeIterator,OddEven,Largest]
  1325.         self.iterator = RandomEdgeIterator
  1326.         self.overlapsText = "*"
  1327.         self.message = " "
  1328.     def makePopupGUI(self):
  1329.         useRandom = Draw.Create(0)
  1330.         pub = []
  1331.         pub.append(("Search", self.search, "Search for non-overlapping net (maybe forever)"))
  1332.         pub.append(("Random", useRandom, "Random style net"))
  1333.         ok = True
  1334.         while ok:
  1335.             ok = Blender.Draw.PupBlock("Unfold", pub)
  1336.             if ok:
  1337.                 if useRandom.val:
  1338.                     self.iterator = RandomEdgeIterator
  1339.                 else:
  1340.                     self.iterator = Curvature
  1341.                 self.unfold()
  1342.     def makeStandardGUI(self):
  1343.         Draw.Register(self.draw, self.keyOrMouseEvent, self.buttonEvent)
  1344.     def installScriptLink(self):
  1345.         print "Adding script link for animation"
  1346.         s = Blender.Scene.GetCurrent().getScriptLinks("FrameChanged")
  1347.         if(s!=None and s.count("frameChanged.py")>0):
  1348.             return
  1349.         try:
  1350.             script = Blender.Text.Get("frameChanged.py")
  1351.         except:
  1352.             script = Blender.Text.New("frameChanged.py")
  1353.             script.write("import Blender\n")
  1354.             script.write("import mesh_unfolder as Unfolder\n")
  1355.             script.write("u = Blender.Registry.GetKey('unfolder')\n")
  1356.             script.write("if u!=None:\n")
  1357.             script.write("\tn = u['net']\n")
  1358.             script.write("\tif(n!=None and n.animates):\n")
  1359.             script.write("\t\tn.unfoldToCurrentFrame()\n")
  1360.         Blender.Scene.GetCurrent().addScriptLink("frameChanged.py", "FrameChanged")
  1361.     def unfold(self):
  1362.         anc = self.ancestors.val
  1363.         n = 0.0
  1364.         s = True
  1365.         self.nOverlaps = 0
  1366.         searchLimit = 10
  1367.         search = 1
  1368.         Draw.Redraw(1)
  1369.         net = None
  1370.         name = None
  1371.         try:
  1372.             self.say("Unfolding...")
  1373.             Draw.Redraw(1)
  1374.             while(s):# and search < searchLimit):
  1375.                 if(net!=None):
  1376.                     name = net.des.name
  1377.                 net = Net.fromSelected(self, name)
  1378.                 net.setAvoidsOverlaps(not(self.overlaps.val))
  1379.                 print
  1380.                 print "Unfolding selected object"
  1381.                 net.edgeIteratorClass = self.iterator
  1382.                 print "Using ", net.edgeIteratorClass
  1383.                 net.animates = self.ani.val
  1384.                 self.diffuse = (self.ancestors.val==0)
  1385.                 net.diffuse = self.diffuse
  1386.                 net.generations = self.ancestors.val
  1387.                 net.noise = self.noise.val
  1388.                 print "even:", net.diffuse, " depth:", net.generations
  1389.                 net.unfold()
  1390.                 n = net.report()
  1391.                 t = "."
  1392.                 if(n<1.0):
  1393.                     t = "Overlaps>="+str(n)
  1394.                 else:
  1395.                     t = "A complete net."
  1396.                 self.nOverlaps = (n>=1)
  1397.                 if(self.nOverlaps):
  1398.                     self.say(self.message+" - unfolding failed - try again ")
  1399.                 elif(not(self.overlaps.val)):
  1400.                     self.say("Success. Complete net - no overlaps ")
  1401.                 else:
  1402.                     self.say("Unfolding complete")
  1403.                 self.ancestors.val = anc
  1404.                 s = (self.search.val and n>=1.0)
  1405.                 dict = Registry.GetKey('unfolder')
  1406.                 if(not(dict)):
  1407.                     dict = {}
  1408.                 dict['net'] = net
  1409.                 Registry.SetKey('unfolder', dict)
  1410.                 if(s):
  1411.                     net = net.clone()
  1412.                 search += 1
  1413.         except(IndexError):
  1414.             self.say("Please select an object to unfold")
  1415.         except:
  1416.             self.say("Problem unfolding selected object - see console for details")
  1417.             print "Problem unfolding selected object:"
  1418.             print sys.exc_info()[1]
  1419.             traceback.print_exc(file=sys.stdout)
  1420.         if(self.ani):
  1421.             if Registry.GetKey('unfolder')==None:
  1422.                 print "no net!"
  1423.                 return
  1424.             Registry.GetKey('unfolder')['net'].sortOutIPOSource()
  1425.             self.installScriptLink()
  1426.         Draw.Redraw(1)
  1427.     def keyOrMouseEvent(self, evt, val):
  1428.         if (evt == Draw.ESCKEY and not val):
  1429.             Draw.Exit()
  1430.     def buttonEvent(self, evt):
  1431.         if (evt == 1):
  1432.             self.unfold()
  1433.         if (evt == 5):
  1434.             try:
  1435.                 Registry.GetKey('unfolder')['net'].setAvoidsOverlaps(self.overlaps.val)
  1436.             except:
  1437.                 pass
  1438.         if (evt == 2):
  1439.             print "Trying to set IPO curve"
  1440.             try:
  1441.                 s = Blender.Object.GetSelected()
  1442.                 if(s!=None):
  1443.                     Registry.GetKey('unfolder')['net'].setIPOSource( s[0] )
  1444.                     print "Set IPO curve"
  1445.                 else:
  1446.                     print "Please select an object to use the IPO of"
  1447.             except:
  1448.                 print "Problem setting IPO source"
  1449.             Draw.Redraw(1)
  1450.         if (evt == 6):
  1451.             Draw.Exit()
  1452.         if (evt == 7):
  1453.             try:
  1454.                 if (Registry.GetKey('unfolder')['net']!=None):
  1455.                     Registry.GetKey('unfolder')['net'].animates = self.ani.val
  1456.                     if(self.ani):
  1457.                         Registry.GetKey('unfolder')['net'].sortOutIPOSource()
  1458.                         self.installScriptLink()
  1459.             except:
  1460.                 print sys.exc_info()[1]
  1461.                 traceback.print_exc(file=sys.stdout)
  1462.             Draw.Redraw(1)
  1463.         if (evt == 19):
  1464.             pass
  1465.         if (evt == 87):
  1466.             try:
  1467.                 if (Registry.GetKey('unfolder')['net']!=None):
  1468.                     Registry.GetKey('unfolder')['net'].assignUVs()
  1469.                     self.say("Assigned UVs")
  1470.             except:
  1471.                 print sys.exc_info()[1]
  1472.                 traceback.print_exc(file=sys.stdout)
  1473.             Draw.Redraw(1)
  1474.         if(evt==91):
  1475.             if( testOverlap() == True):
  1476.                 self.nOverlaps = 1
  1477.             else:
  1478.                 self.nOverlaps = 0
  1479.             Draw.Redraw(1)
  1480.         if(evt==233):
  1481.             f1 = Poly.fromBlenderFace(Blender.Object.GetSelected()[0].getData().faces[0])
  1482.             f2 = Poly.fromBlenderFace(Blender.Object.GetSelected()[1].getData().faces[0])
  1483.             print
  1484.             print Blender.Object.GetSelected()[0].getName()
  1485.             print Blender.Object.GetSelected()[1].getName()
  1486.             print f1.intersects2D(f2)
  1487.             print f2.intersects2D(f1)
  1488.         if(evt==714):
  1489.             Net.unfoldAll(self)
  1490.             Draw.Redraw(1)
  1491.         if(evt==713):
  1492.             self.iterator = self.iterators[self.shape.val]
  1493.             Draw.Redraw(1)
  1494.         if(evt==92):
  1495.             if( testContains() == True):
  1496.                 self.nOverlaps = 1
  1497.             else:
  1498.                 self.nOverlaps = 0
  1499.             Draw.Redraw(1)
  1500.         if(evt==104):
  1501.             try:
  1502.                 filename = "net.svg"
  1503.                 s = Blender.Object.GetSelected()
  1504.                 if(s!=None and len(s)>0):
  1505.                     filename = s[0].getName()+".svg"
  1506.                 else:
  1507.                     if (Registry.GetKey('unfolder')['net']!=None):
  1508.                         filename = Registry.GetKey('unfolder')['net'].des.name
  1509.                         if(filename==None):
  1510.                             filename="net.svg"
  1511.                         else:
  1512.                             filename=filename+".svg"
  1513.                 Window.FileSelector(SVGExporter.fileSelected, "Select filename", filename)
  1514.             except:
  1515.                 print "Problem exporting SVG"
  1516.                 traceback.print_exc(file=sys.stdout)
  1517.         if(evt==107):
  1518.             try:
  1519.                 Window.FileSelector(NetHandler.fileSelected, "Select file")
  1520.             except:
  1521.                 print "Problem importing SVG"
  1522.                 traceback.print_exc(file=sys.stdout)
  1523.     def say(self, m):
  1524.         self.message = m
  1525.         Draw.Redraw(1)
  1526.         Window.Redraw(Window.Types.SCRIPT)
  1527.     def draw(self):
  1528.         cw = 64
  1529.         ch = 16
  1530.         l = FlowLayout(32, cw, ch, 350, 64)
  1531.         l.y = 70
  1532.         self.search = Draw.Toggle("search",     19,   l.nx(), l.ny(), l.cw, l.ch, self.search.val, "Search for non-overlapping mesh (potentially indefinitely)")
  1533.         self.overlaps = Draw.Toggle("overlaps",   5,   l.nx(), l.ny(), l.cw, l.ch, self.overlaps.val, "Allow overlaps / avoid overlaps - if off, will not place overlapping faces")
  1534.         self.ani = Draw.Toggle("ani",       7,   l.nx(), l.ny(), l.cw, l.ch, self.ani.val, "Animate net")
  1535.         Draw.Button("uv",               87,   l.nx(), l.ny(), l.cw, l.ch, "Assign net as UV to source mesh (overwriting existing UV)")
  1536.         Draw.Button("Unfold",           1, l.nx(), l.ny(), l.cw, l.ch, "Unfold selected mesh to net")
  1537.         Draw.Button("save",             104,   l.nx(), l.ny(), l.cw, l.ch,  "Save net as SVG")
  1538.         Draw.Button("load",             107,   l.nx(), l.ny(), l.cw, l.ch,  "Load net from SVG")
  1539.         #Draw.Button("test",             233,   l.nx(), l.ny(), l.cw, l.ch,  "test")
  1540.         # unfolding enthusiasts - try uncommenting this
  1541.         self.ancestors = Draw.Number("depth", 654,        l.nx(), l.ny(), cw, ch, self.ancestors.val, 0, 9999,  "depth of branching 0=diffuse")
  1542.         #self.noise = Draw.Number("noise", 631,        l.nx(), l.ny(), cw, ch, self.noise.val, 0.0, 1.0,  "noisyness of branching")
  1543.         #Draw.Button("UnfoldAll",           714, l.nx(), l.ny(), l.cw, l.ch, "Unfold all meshes and save their nets")
  1544.         options = "order %t|random %x0|brightest %x1|curvature %x2|winding %x3| 1010 %x4|largest %x5"
  1545.         self.shape = Draw.Menu(options, 713,  l.nx(), l.ny(), cw, ch, self.shape.val, "shape of net")
  1546.         Draw.Button("exit",         6,   l.nx(), l.ny(), l.cw, l.ch, "exit")
  1547.         BGL.glClearColor(0.3, 0.3, 0.3, 1)
  1548.         BGL.glColor3f(0.3,0.3,0.3)
  1549.         l.newLine()
  1550.         BGL.glRasterPos2i(32, 100)
  1551.         Draw.Text(self.message)
  1552.  
  1553. class FlowLayout:
  1554.     def __init__(self, margin, cw, ch, w, h):
  1555.         self.x = margin-cw-4
  1556.         self.y = margin
  1557.         self.cw = cw
  1558.         self.ch = ch
  1559.         self.width = w
  1560.         self.height = h
  1561.         self.margin = margin
  1562.     def nx(self):
  1563.         self.x+=(self.cw+4)
  1564.         if(self.x>self.width):
  1565.             self.x = self.margin
  1566.             self.y-=self.ch+4
  1567.         return self.x
  1568.     def ny(self):
  1569.         return self.y
  1570.     def newLine(self):
  1571.         self.y-=self.ch+self.margin
  1572.         self.x = self.margin
  1573.  
  1574. # if xml is None, then dont bother running the script
  1575. if xml:
  1576.     try:
  1577.         sys.setrecursionlimit(10000)
  1578.         gui = GUI()
  1579.         gui.makeStandardGUI()
  1580.         #gui.makePopupGUI()
  1581.     except:
  1582.         traceback.print_exc(file=sys.stdout)
  1583.